﻿#include <algorithm>
#include <gtest/gtest.h>
#include <iostream>
#include <learn/mesh/mesh3d.h>
#include <learn/topo/topo_builder.h>
#include <numeric>
#include <random>
#include <vector>

using namespace xi;
using namespace xi::topo;
using namespace xi::mesh;
using Topo_Mesh = Half_Mesh;

#define TIMES 10

class TestMesh : public ::testing::Test
{
  protected:
    void SetUp() override
    {
    }

    void TearDown() override{
        // nothing
    };

    void CreateFace3(Topo_Mesh &mesh, int lower, int higher, const std::vector<std::vector<CHandle>> &chandles,
                     std::vector<std::vector<int>> &tag)
    {
        for (int i = 0; i < tag.size(); i++)
            tag[i][i] = 1;

        for (int i = lower; i < higher; i++)
        {
            for (int j = lower; j < higher; j++)
            {
                for (int k = lower; k < higher; k++)
                {
                    if (tag[i][j] == 0 && tag[j][k] == 0 && tag[k][i] == 0)
                    {
                        tag[i][j] = 1;
                        tag[j][k] = 1;
                        tag[k][i] = 1;
                        mesh.create_face({chandles[i][j], chandles[j][k], chandles[k][i]});
                    }
                }
            }
        }
    }

    void CreateFace4(Topo_Mesh &mesh, int lower, int higher, const std::vector<std::vector<CHandle>> &chandles,
                     std::vector<std::vector<int>> &tag)
    {
        for (int i = 0; i < tag.size(); i++)
            tag[i][i] = 1;

        for (int i = lower; i < higher; i++)
        {
            for (int j = lower; j < higher; j++)
            {
                for (int k = lower; k < higher; k++)
                {
                    for (int l = lower; l < higher; l++)
                    {
                        std::set<int> s = {i, j, k, l};
                        if (s.size() == 4 && tag[i][j] == 0 && tag[j][k] == 0 && tag[k][l] == 0 && tag[l][i] == 0)
                        {
                            tag[i][j] = 1;
                            tag[j][k] = 1;
                            tag[k][l] = 1;
                            tag[l][i] = 1;
                            mesh.create_face({chandles[i][j], chandles[j][k], chandles[k][l], chandles[l][i]});
                        }
                    }
                }
            }
        }
    }

    void CreateFace5(Topo_Mesh &mesh, int lower, int higher, const std::vector<std::vector<CHandle>> &chandles,
                     std::vector<std::vector<int>> &tag)
    {
        for (int i = 0; i < tag.size(); i++)
            tag[i][i] = 1;

        for (int i = lower; i < higher; i++)
        {
            for (int j = lower; j < higher; j++)
            {
                for (int k = lower; k < higher; k++)
                {
                    for (int l = lower; l < higher; l++)
                    {
                        for (int m = lower; m < higher; m++)
                        {
                            std::set<int> s = {i, j, k, l, m};
                            if (s.size() == 5 && tag[i][j] == 0 && tag[j][k] == 0 && tag[k][l] == 0 && tag[l][m] == 0 &&
                                tag[m][i] == 0)
                            {
                                tag[i][j] = 1;
                                tag[j][k] = 1;
                                tag[k][l] = 1;
                                tag[l][m] = 1;
                                tag[m][i] = 1;
                                mesh.create_face(
                                    {chandles[i][j], chandles[j][k], chandles[k][l], chandles[l][m], chandles[m][i]});
                            }
                        }
                    }
                }
            }
        }
    }

    void CreateTotalTriangleMesh(Topo_Mesh &mesh, int N)
    {
        std::vector<VHandle> vhandles(N);
        for (int i = 0; i < N; i++)
            vhandles[i] = mesh.create_vertex();

        std::vector<int> indices(N);
        std::iota(indices.begin(), indices.end(), 0);

        std::vector<std::vector<CHandle>> chandles(N, std::vector<CHandle>(N));
        std::vector<std::vector<int>> tag(N, std::vector<int>(N, 0));
        for (int i = 0; i < N; i++)
        {
            for (int j = i + 1; j < N; j++)
            {
                auto co = mesh.create_edge(vhandles[indices[i]], vhandles[indices[j]]);
                chandles[i][j] = co;
                chandles[j][i] = co->twin();
            }
        }

        CreateFace3(mesh, 0, N, chandles, tag);
    }

    void CreateRandomTriangleMesh(Topo_Mesh &mesh, int N)
    {
        std::vector<VHandle> vhandles(N);
        for (int i = 0; i < N; i++)
            vhandles[i] = mesh.create_vertex();

        std::vector<int> indices(N);
        std::iota(indices.begin(), indices.end(), 0);
        std::shuffle(indices.begin(), indices.end(), std::mt19937{});

        int M = rand() % N;
        std::vector<std::vector<CHandle>> chandles(M, std::vector<CHandle>(M));
        std::vector<std::vector<int>> tag(M, std::vector<int>(M, 0));
        for (int i = 0; i < M; i++)
        {
            for (int j = i + 1; j < M; j++)
            {
                auto co = mesh.create_edge(vhandles[indices[i]], vhandles[indices[j]]);
                chandles[i][j] = co;
                chandles[j][i] = co->twin();
            }
        }

        int K = M == 0 ? 0 : rand() % M;
        CreateFace3(mesh, 0, K, chandles, tag);
    }

    void CreateRandomMixtureMesh(Topo_Mesh &mesh, int N)
    {
        std::vector<VHandle> vhandles(N);
        for (int i = 0; i < N; i++)
            vhandles[i] = mesh.create_vertex();

        std::vector<int> indices(N);
        std::iota(indices.begin(), indices.end(), 0);
        std::shuffle(indices.begin(), indices.end(), std::mt19937{});

        int M = rand() % N;
        std::vector<std::vector<CHandle>> chandles(M, std::vector<CHandle>(M));
        std::vector<std::vector<int>> tag(M, std::vector<int>(M, 0));
        for (int i = 0; i < M; i++)
        {
            for (int j = i + 1; j < M; j++)
            {
                auto co = mesh.create_edge(vhandles[indices[i]], vhandles[indices[j]]);
                chandles[i][j] = co;
                chandles[j][i] = co->twin();
            }
        }

        int K = M == 0 ? 0 : rand() % M;
        CreateFace3(mesh, 0, K / 3, chandles, tag);
        CreateFace4(mesh, K / 3, K / 2, chandles, tag);
        // CreateFace5(mesh, K / 2, K, chandles, tag);

        std::cout << "K: " << K << std::endl;
    }
};

// 全连通的网格
TEST_F(TestMesh, CreateTotal)
{
    for (int i = 0; i < TIMES; i++)
    {
        Topo_Mesh mesh;
        CreateTotalTriangleMesh(mesh, 100);
    }

    // std::cout << mesh.vertex_num() << std::endl;
    // std::cout << mesh.edge_num() << std::endl;
    // std::cout << mesh.face_num() << std::endl;
}

TEST_F(TestMesh, CreatePartial)
{
    for (int i = 0; i < TIMES; i++)
    {
        Topo_Mesh mesh;
        CreateRandomTriangleMesh(mesh, 100);
    }

    for (int i = 0; i < TIMES; i++)
    {
        Topo_Mesh mesh;
        CreateRandomMixtureMesh(mesh, 100);
    }

    // std::cout << mesh.vertex_num() << std::endl;
    // std::cout << mesh.edge_num() << std::endl;
    // std::cout << mesh.face_num() << std::endl;
}

TEST_F(TestMesh, RemoveTotal)
{
    for (int i = 0; i < TIMES; i++)
    {
        Topo_Mesh mesh;
        CreateRandomMixtureMesh(mesh, 100);

        // 根据前面两个测试创建的网格，随机删除一些顶点、边、面
        int N = mesh.vertex_num();
        int M = mesh.edge_num();
        int K = mesh.face_num();

        N = rand() % N;
        M = M == 0 ? 0 : rand() % M;
        K = K == 0 ? 0 : rand() % K;

        while (!mesh.empty())
        {
            auto vit = mesh.vertices().begin();
            auto eit = mesh.edges().begin();
            auto fit = mesh.faces().begin();

            int tag = rand() % 3;
            if (tag == 0 && vit != mesh.vertices().end())
                mesh.remove(*vit);
            else if (tag == 1 && eit != mesh.edges().end())
                mesh.remove(*eit);
            else if (tag == 2 && fit != mesh.faces().end())
                mesh.remove(*fit);
        }
    }
}

TEST_F(TestMesh, Mesh3D)
{
    Mesh3D<3> mesh;
    std::vector<VHandle> vhandles;
    vhandles.push_back(mesh.create_vertex(Vec3f{0.0f, 0.0f, 0.0f}));
    vhandles.push_back(mesh.create_vertex(Vec3f{1.0f, 0.0f, 0.0f}));
    vhandles.push_back(mesh.create_vertex(Vec3f{0.0f, 1.0f, 0.0f}));
    vhandles.push_back(mesh.create_vertex(Vec3f{0.0f, 0.0f, 1.0f}));

    std::vector<FHandle> fhandles;
    fhandles.push_back(mesh.create_face(vhandles[0], vhandles[1], vhandles[2]));
    fhandles.push_back(mesh.create_face(vhandles[0], vhandles[3], vhandles[1]));

    // 测试顶点属性
    {
        VProperty_Handle<int> prop("test");
        vhandles[0]->add_property(prop);
        vhandles[0]->property(prop) = 10;
        EXPECT_EQ(vhandles[0]->property(prop), 10);
        EXPECT_TRUE(vhandles[0]->has_property(prop));

        vhandles[0]->remove_property(prop);
        EXPECT_FALSE(vhandles[0]->has_property(prop));
    }

    // 测试面属性
    {
        FProperty_Handle<int> prop("test");
        fhandles[0]->add_property(prop);
        fhandles[0]->property(prop) = 10;
        EXPECT_EQ(fhandles[0]->property(prop), 10);
        EXPECT_TRUE(fhandles[0]->has_property(prop));

        fhandles[0]->remove_property(prop);
        EXPECT_FALSE(fhandles[0]->has_property(prop));
    }

    // 测试顶点标记
    {
        mesh.request_vertex_ids();
        for (auto vh : mesh.vertices())
        {
            mesh.vertex_id(vh) = 10;
        }
        mesh.remove_vertex_ids();
    }

    // 测试法向
    {
        mesh.request_vertex_normals();
        mesh.request_face_normals();
        mesh.update_face_normals();
        mesh.update_vertex_normals();
        mesh.remove_vertex_normals();
        mesh.remove_face_normals();
    }
}

TEST_F(TestMesh, TopoShell)
{
    // Topo_Mesh mesh;
    // CreateTotalTriangleMesh(mesh, 4);

    // Topo_Builder builder;
    // auto wire = builder.create_wire();
}

TEST_F(TestMesh, TopoBuilder)
{
}

// TEST_F(TestMesh, TriangleMesh)
// {
//     Mesh3D<3> mesh;
//     std::vector<VHandle> vhandles;
//     vhandles.push_back(mesh.create_vertex(Vec3f{0.0f, 0.0f, 0.0f}));
//     vhandles.push_back(mesh.create_vertex(Vec3f{1.0f, 0.0f, 0.0f}));
//     vhandles.push_back(mesh.create_vertex(Vec3f{0.0f, 1.0f, 0.0f}));
//     vhandles.push_back(mesh.create_vertex(Vec3f{0.0f, 0.0f, 1.0f}));

//     std::vector<FHandle> fhandles;
//     fhandles.push_back(mesh.create_face(vhandles[0], vhandles[1], vhandles[2]));
//     fhandles.push_back(mesh.create_face(vhandles[0], vhandles[3], vhandles[1]));

//     Topo_Builder builder;
//     {
//         auto nf = builder.adjacent_faces(vhandles[2]);
//         auto it = std::find(nf.begin(), nf.end(), fhandles[0]);
//         EXPECT_TRUE(it != nf.end());
//         EXPECT_EQ(nf.size(), 1);
//     }

//     {
//         auto nv = builder.adjacent_vertices(vhandles[2]);
//         auto it = std::find(nv.begin(), nv.end(), vhandles[0]);
//         EXPECT_TRUE(it != nv.end());
//         it = std::find(nv.begin(), nv.end(), vhandles[1]);
//         EXPECT_TRUE(it != nv.end());
//         EXPECT_EQ(nv.size(), 2);
//     }
// }